package com.personal.system.service.impl.approve;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.collection.ListUtil;
import cn.hutool.core.lang.UUID;
import cn.hutool.core.util.ArrayUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.personal.common.result.Result;
import com.personal.model.system.SysUser;
import com.personal.system.entity.approval.*;
import com.personal.system.exception.GlobalException;
import com.personal.system.mapper.act.ApproveLogMapper;
import com.personal.system.service.act.AuditRuleDetailService;
import com.personal.system.service.act.AuditRuleService;
import com.personal.system.service.sys.SysUserService;
import com.personal.system.service.approve.ApproveDetailService;
import com.personal.system.service.approve.ApproveProcessCallBack;
import com.personal.system.service.approve.ApproveWorkService;
import com.personal.system.service.approve.CostQuoteRulesService;
import com.personal.system.config.SecurityUtil;
import lombok.extern.slf4j.Slf4j;
import org.activiti.engine.HistoryService;
import org.activiti.engine.RuntimeService;
import org.activiti.engine.TaskService;
import org.activiti.engine.runtime.ProcessInstance;
import org.activiti.engine.runtime.ProcessInstanceBuilder;
import org.activiti.engine.runtime.ProcessInstanceQuery;
import org.activiti.engine.task.Task;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * 审批通用类
 */
@Slf4j
@Service
public class ApproveWorkServiceImpl implements ApproveWorkService {

    @Resource
    private SecurityUtil securityUtil;

    @Resource
    private AuditRuleService ruleService;
    @Resource
    private AuditRuleDetailService ruleDetailService;

    @Resource
    private CostQuoteRulesService costQuoteService;

    @Resource
    private ApproveDetailService detailService;
    @Resource
    private ApproveLogMapper approveLogMapper;

    @Resource
    private SysUserService userService;

    @Resource
    private RuntimeService runtimeService;
    @Resource
    private TaskService taskService;
    @Resource
    private HistoryService historyService;

    private Map<String, ApproveProcessCallBack> approveProcessMap = new HashMap<>();

    @Autowired
    public void setOrderHandlerList(List<ApproveProcessCallBack> approveProcessCallBackList) {
        for (ApproveProcessCallBack approveProcessCallBack : approveProcessCallBackList) {
            approveProcessMap.put(approveProcessCallBack.getRuleClassEnum().getCode(), approveProcessCallBack);
        }
    }

    // 提交审批操作
    @Override
    public Result submitApprove(ApproveVo submitApproveVo) {

        submitApproveVo.setOptType("01");

        String[] businessIds = submitApproveVo.getBusinessIds();
        List<String> businessList = ListUtil.toList(businessIds);
        Map<String, AuditRule> businessIdRuleMap = new HashMap<>();

        ApproveProcessCallBack processCallBack = approveProcessMap.get(submitApproveVo.getRuleClass());

        for (String businessId : businessIds) {
            String compId = processCallBack.getAuditRuleByBusiness(businessId);
            AuditRule auditRuleDb = getDelayAuditRule(compId, submitApproveVo);
            if (null == auditRuleDb) {
                throw new GlobalException(404,"审批规则不存在");
            }
            // 是否自动审批
            this.isAutoAudit(submitApproveVo,businessList,businessId,businessIdRuleMap,auditRuleDb);
        }

        log.info("审批流businessIds赋值前数据:{}" , businessIds);
        businessIds = businessList.toArray(new String[businessList.size()]);
        log.info("审批流businessIds赋值后数据:{}" , businessIds);

        if (ArrayUtil.isEmpty(businessIds))  return Result.ok();

        for (String businessId : businessIds) {
            AuditRule auditRule = businessIdRuleMap.remove(businessId);
            LambdaQueryWrapper<ApproveDetail> eq = Wrappers.lambdaQuery(ApproveDetail.class)
                    .eq(ApproveDetail::getApproveNo, businessId)
                    .eq(ApproveDetail::getRuleClass, submitApproveVo.getRuleClass());
            detailService.remove(eq);

            // 上面先删除再插入审批人信息到审批表
            insertApproveDetail(businessId, auditRule);

            // 提交到Activiti
            String userId = submitApproveVo.getUserId();
            String ruleNo = auditRule.getRuleNo();
            submitForActiviti(ruleNo, businessId, userId, submitApproveVo);
        }

        // 回调方法了
        ApproveProcessCallBack callBack = approveProcessMap.get(submitApproveVo.getRuleClass());

        BusinessApproveVo businessApproveVo = new BusinessApproveVo();
        businessApproveVo.setBusinessIds(businessIds);
        businessApproveVo.setUserId(submitApproveVo.getUserId());
        for (String businessId : businessIds) {
            LambdaQueryWrapper<ApproveDetail> eq = Wrappers.lambdaQuery(ApproveDetail.class)
                    .eq(ApproveDetail::getApprovalStatus, "02").eq(ApproveDetail::getApproveNo, businessId);
            ApproveDetail one = detailService.getOne(eq);
            if (null != one){
                businessApproveVo.addApproveDetail(businessId, one);
            }
        }

        return callBack.submitForProcess(businessApproveVo);
    }

    private AuditRule getDelayAuditRule(String compId, ApproveVo approveVo) {

        AuditRule rule = new AuditRule();
        rule.setCompId(compId);
        rule.setApprovalModule(approveVo.getApprovalModule());
        rule.setRuleClass(approveVo.getRuleClass());
        if (StrUtil.isNotBlank(approveVo.getAuditType())){
            rule.setAuditType(approveVo.getAuditType());
        }
        rule.setAuditSubmitUser(approveVo.getUserId());

        AuditRule auditRule = ruleService.getAuditRule(rule);
        if (null != auditRule) return auditRule;

        rule.setAuditTrigger(String.join(",",approveVo.getDepartmentIdList()));

        return ruleService.getAuditRule(rule);
    }

    private ApproveDetail insertApproveDetail(String approveNo, AuditRule auditRule) {
        // 查询审批的人
        LambdaQueryWrapper<AuditRuleDetail> eq = Wrappers.lambdaQuery(AuditRuleDetail.class)
                .eq(AuditRuleDetail::getProcessType, "02")
                .eq(AuditRuleDetail::getRuleId, auditRule.getRuleId()).orderByAsc(AuditRuleDetail::getSort);
        List<AuditRuleDetail> details = ruleDetailService.list(eq);

        ApproveDetail resultApproveDetail = new ApproveDetail();
        for (int i = 0; i < details.size(); i++) {
            AuditRuleDetail item = details.get(i);

            ApproveDetail approveDetail = new ApproveDetail();
            approveDetail.setId(UUID.fastUUID().toString(true));
            approveDetail.setApproveNo(approveNo);
            approveDetail.setRuleNo(auditRule.getRuleNo());
            approveDetail.setRuleClass(auditRule.getRuleClass());
            approveDetail.setAuditType(auditRule.getAuditType());
            approveDetail.setAddSignType("01");
            approveDetail.setApprovalSort(i + 1);

            String roleType = item.getRoleType();
            if ("01".equals(roleType)) {
                SysUser sysUser = userService.getById(item.getRole());
                if (sysUser != null) {
                    approveDetail.setApprover(sysUser.getId());
                    approveDetail.setApproverName(sysUser.getName());
                    approveDetail.setDepartId(sysUser.getDeptId() + "");
                    approveDetail.setDepartName(sysUser.getDeptName());
                }
                approveDetail.setApproverType("01");
            }
            // 扩展:如果是角色设置审批人是角色名,再存储角色的部门，部门也类似

            approveDetail.setType("01");
            approveDetail.setCreater("system");
            approveDetail.setCreateTime(new Date());

            if (i == 0) {
                approveDetail.setApprovalStatus("02");
                resultApproveDetail = approveDetail;
            } else {
                approveDetail.setApprovalStatus("01");
            }

            if (approveDetail.getDepartId() == null) approveDetail.setDepartId(StrUtil.EMPTY);

            if (approveDetail.getDepartName() == null) approveDetail.setDepartName(StrUtil.EMPTY);

            detailService.save(approveDetail);
        }

        return resultApproveDetail;
    }

    private void submitForActiviti(String ruleNo, String businessId, String userId, ApproveVo approveVo) {

        final String businessKey = approveVo.getRuleClass().concat("_").concat(businessId);

        ProcessInstanceQuery query = runtimeService.createProcessInstanceQuery();
        ProcessInstanceQuery processQuery = query.processDefinitionKey(businessId).processInstanceBusinessKey(businessKey);
        try {
            // 先删除运行时的实例和历史记录
            List<ProcessInstance> insList = processQuery.list();
            for (ProcessInstance instance : insList) {
                String id = instance.getProcessInstanceId();
                runtimeService.deleteProcessInstance(id,"delete");
                historyService.deleteHistoricProcessInstance(id);
            }
        } catch (Exception e) {
            log.error("删除实例异常：{}", e);
            e.printStackTrace();
        }

        // 指定审批人后，启动一个实例
        Map<String, Object> map = new HashMap<>();
        map.put("applyUserId", userId);
        // 检查processKey、businessId是否已存在流程实例
        ProcessInstance processInstance = processQuery.singleResult();
        if (null != processInstance){
            throw new GlobalException(404,"已存在相同类型、相同业务ID的流程实例!");
        }
        // 创建流程实例并启动
        ProcessInstanceBuilder instanceBuilder = runtimeService.createProcessInstanceBuilder();
        ProcessInstanceBuilder instance = instanceBuilder.tenantId(approveVo.getTenantId())
                .processDefinitionKey(ruleNo).businessKey(businessKey).variables(map);
        ProcessInstance start = instance.start();
        String instanceId = start.getId();

        if (StrUtil.isNotBlank(instanceId)){
            Task task = taskService.createTaskQuery().processInstanceId(instanceId).singleResult();
            // 如果是提交任务直接完成
            if (null != task && "submitUserTask".equals(task.getTaskDefinitionKey())){
                // Authentication.setAuthenticatedUserId(userId);
                // 添加说明，拾取任务，完成任务
                taskService.addComment(task.getId(), task.getProcessInstanceId(), "提交审批单");
                taskService.claim(task.getId(), userId);
                taskService.complete(task.getId(), null);
            }
        }

        // 添加提交日志
        ApproveLog approveLog = new ApproveLog();
        approveLog.setInstanceId(instanceId);
        approveLog.setRuleNo(ruleNo);
        approveLog.setBusinessId(businessId);
        approveLog.setCreater(userId);
        approveLog.setCreaterName(securityUtil.getUserName());
        approveLog.setCreateTime(new Date());
        approveLog.setType("01");
        approveLog.setRuleClass(approveVo.getRuleClass());
        String auditType = approveVo.getAuditType();
        if (StrUtil.isNotBlank(auditType))
            approveLog.setAuditType(Integer.valueOf(approveVo.getAuditType()));
        approveLogMapper.insert(approveLog);
    }

    @Override
    public void approve(ApproveVo userApproval) {
        userApproval.setOptType("02");
        String[] businessIds = userApproval.getBusinessIds();
        for (String businessId : businessIds) {
            LambdaQueryWrapper<ApproveDetail> qw = Wrappers.lambdaQuery(ApproveDetail.class)
                    .eq(ApproveDetail::getApproveNo, businessId).eq(ApproveDetail::getApprovalStatus,"02");
            ApproveDetail detail = detailService.getOne(qw);
            if (null == detail){
                return;
            }
            // 校验是否可审批
            if (!"01".equals(detail.getApproverType()) && !userApproval.getUserId().equals(detail.getApprover())){
                throw new GlobalException(404,"不能审批操作！");
            }

            // 是否同意
            String result = userApproval.getApprovalResult();

            LambdaQueryWrapper<ApproveDetail> qw2 = Wrappers.lambdaQuery(ApproveDetail.class)
                    .eq(ApproveDetail::getApproveNo, businessId).orderByAsc(ApproveDetail::getApprovalSort);
            List<ApproveDetail> detailList = detailService.list(qw2);

            if ("01".equals(result)) {
                // 通过
                ApproveDetail nextApproveDetail = null;
                for (ApproveDetail item : detailList) {
                    // 如果当前状态是审批中就变成完成
                    if ("02".equals(item.getApprovalStatus())){
                        item.setApprovalStatus("03");
                        item.setApprovalRemark(userApproval.getApprovalComment());
                        item.setApprovalTime(new Date());
                        item.setUpdater(userApproval.getUserId());
                        item.setUpdateTime(new Date());
                        item.setHandlerUserId(userApproval.getUserId());
                        detailService.updateById(item);
                        // 提交到 Activiti 中审批
                        this.approveForActiviti(userApproval, item);
                    } else if ("01".equals(item.getApprovalStatus())){
                        // 如果当前状态是未提交 下一级审批流状态变成审批中
                        if (item.getApprovalSort() == detail.getApprovalSort() + 1){
                            item.setApprovalStatus("02");
                            detailService.updateById(item);
                            nextApproveDetail = item;
                        }
                    }
                }

                if (nextApproveDetail != null) {
                    // 还要继续审批,把信息回传到业务方法
                    ApproveProcessCallBack callBack = approveProcessMap.get(userApproval.getRuleClass());
                    BusinessApproveVo businessApproveVo = new BusinessApproveVo();
                    businessApproveVo.setBusinessIds(new String[] {businessId});
                    businessApproveVo.setUserId(userApproval.getUserId());
                    businessApproveVo.setApprovalOpinion(userApproval.getApprovalComment());
                    businessApproveVo.setApprovalStatus(result);
                    businessApproveVo.addApproveDetail(businessId, nextApproveDetail);
                    callBack.modifyPendUserForProcess(businessApproveVo);
                } else {
                    // 审批完成,通知业务方法
                    ApproveProcessCallBack approveCallBack = approveProcessMap.get(userApproval.getRuleClass());
                    BusinessApproveVo businessApproveVo = new BusinessApproveVo();
                    businessApproveVo.setBusinessIds(new String[] {businessId});
                    businessApproveVo.setUserId(userApproval.getUserId());
                    businessApproveVo.setApprovalOpinion(userApproval.getApprovalComment());
                    businessApproveVo.setApprovalStatus(result);
                    approveCallBack.completedForProcess(businessApproveVo);
                }

            } else {
                // 驳回操作
                for (int i = 0; i < detailList.size(); i++) {
                    ApproveDetail detailItem = detailList.get(i);
                    detailItem.setId(detailItem.getId());
                    detailItem.setApprovalRemark(StrUtil.EMPTY);
                    detailItem.setApprovalTime(null);
                    // 如果是第一个审批的人驳回,变审批中,其它位置的变未提交
                    if (i == 0){
                        detailItem.setApprovalStatus("02");
                    } else {
                        detailItem.setApprovalStatus("01");
                    }
                    detailService.updateById(detailItem);
                }

                ApproveProcessCallBack callBack = approveProcessMap.get(userApproval.getRuleClass());
                BusinessApproveVo businessApproveVo = new BusinessApproveVo();
                businessApproveVo.setBusinessIds(new String[] {businessId});
                businessApproveVo.setUserId(userApproval.getUserId());
                businessApproveVo.setApprovalOpinion(userApproval.getApprovalComment());
                businessApproveVo.setApprovalStatus(result);
                callBack.rejectedForProcess(businessApproveVo);

                approveForActiviti(userApproval, detail);
            }

        }
    }

    private void approveForActiviti(ApproveVo userApproval, ApproveDetail approveDetail) {
        final String businessId = approveDetail.getApproveNo();
        final String ruleNo = approveDetail.getRuleNo();
        final String comment = userApproval.getApprovalComment();
        final String approve = approveDetail.getApprover();
        final String ruleClass = userApproval.getRuleClass();
        final String businessKey = ruleClass.concat("_").concat(businessId);

        ProcessInstance processInstance = this.getProcessInstance(ruleNo, businessKey);

        // 增加审批日志
        ApproveLog approveLog = new ApproveLog();
        approveLog.setRuleNo(ruleNo);
        approveLog.setBusinessId(businessId);
        approveLog.setCreater(userApproval.getUserId());
        approveLog.setApprovalDetailId(approveDetail.getId());
        approveLog.setApprovalName(approveDetail.getApproverName());
        approveLog.setCreaterName(securityUtil.getUserName());
        approveLog.setCreateTime(new Date());
        approveLog.setType(userApproval.getOptType());
        approveLog.setRuleClass(userApproval.getRuleClass());
        if (StrUtil.isNotBlank(userApproval.getAuditType())) {
            approveLog.setAuditType(Integer.valueOf(userApproval.getAuditType()));
        }

        approveLog.setApprovalRemark(userApproval.getApprovalComment());
        approveLog.setApprovalStatus(userApproval.getApprovalResult());

        if (processInstance != null ) {
            String instanceId = processInstance.getProcessInstanceId();
            Task task = taskService.createTaskQuery().processInstanceId(instanceId).singleResult();
            if (null != task && StrUtil.isNotBlank(comment) && !"02".equals(approveDetail.getAddSignType())){
                // 添加任务的备注内容
                taskService.addComment(task.getId(),task.getProcessInstanceId(),comment);
                Map<String, Object> params = new HashMap<>();
                params.put("result", "0");
                if ("01".equals(userApproval.getApprovalResult())) {
                    params.put("result", "1");
                }
                // 拾取任务 完成任务
                taskService.claim(task.getId(),approve);
                taskService.complete(task.getId(),params);

                approveLog.setTaskId(task.getId());
                approveLog.setInstanceId(instanceId);
                approveLogMapper.insert(approveLog);
            } else {
                approveLog.setTaskId(StrUtil.EMPTY);
                approveLog.setInstanceId(StrUtil.EMPTY);
                approveLogMapper.insert(approveLog);
                log.info("用户任务未找到 businessKey={},instanceId={}", businessKey, instanceId);
            }
        } else {
            approveLog.setTaskId(StrUtil.EMPTY);
            approveLog.setInstanceId(StrUtil.EMPTY);
            approveLogMapper.insert(approveLog);
            log.info("流程实例未找到 businessKey={}", businessKey);
        }

    }

    private ProcessInstance getProcessInstance(String ruleNo, String businessKey) {
        ProcessInstance processInstance = null;
        List<ProcessInstance> processInstanceList = runtimeService.createProcessInstanceQuery()
                    .processDefinitionKey(ruleNo).processInstanceBusinessKey(businessKey).list();
        if (!processInstanceList.isEmpty()) {
            for (int i = 0; i < processInstanceList.size(); i++) {
                if (i == 0) {
                    processInstance = processInstanceList.get(i);
                } else {
                    try {
                        String instanceId = processInstanceList.get(i).getProcessInstanceId();
                        // 删除运行时流程实例
                        runtimeService.deleteProcessInstance(instanceId, "delete");
                        // 删除历史流程实例日志
                        historyService.deleteHistoricProcessInstance(instanceId);
                    } catch (Exception e) {
                        log.error("删除运行时流程实例异常!", e);
                    }
                }
            }
        }
        return processInstance;
    }

    private void isAutoAudit(ApproveVo approveVo, List<String> businessIds, String businessId,
                             Map<String, AuditRule> businessIdRuleMap,  AuditRule auditRuleDb) {
        String ruleId = auditRuleDb.getRuleId();
        List<AuditRuleDetail> details = ruleDetailService.list(Wrappers.lambdaQuery(AuditRuleDetail.class)
                .eq(AuditRuleDetail::getRuleId, ruleId).eq(AuditRuleDetail::getProcessType,"02"));
        if (CollUtil.isEmpty(details)) {
            ApproveVo approveAutoVo = new ApproveVo();
            BeanUtil.copyProperties(approveVo, approveAutoVo);
            approveAutoVo.setBusinessIds(new String[]{businessId});
            autoAudit(approveAutoVo);
            log.info("自动审批前剩余的ID:{}" , businessIds);
            businessIds.removeIf(e -> e.equals(businessId));
            log.info("自动审批后剩余的ID:{}" , businessIds);
        } else {
            businessIdRuleMap.put(businessId, auditRuleDb);
        }
    }

    private Result autoAudit(ApproveVo approveVo) {
        ApproveProcessCallBack approveProcessCallBack = approveProcessMap.get(approveVo.getRuleClass());
        String[] businessIds = approveVo.getBusinessIds();
        BusinessApproveVo businessApproveVo = new BusinessApproveVo();
        businessApproveVo.setBusinessIds(businessIds);
        businessApproveVo.setUserId(approveVo.getUserId());
        businessApproveVo.setApprovalOpinion(approveVo.getApprovalComment());
        businessApproveVo.setApprovalStatus(approveVo.getApprovalResult());

        // 增加自动审批记录
        ApproveLog approveLog = new ApproveLog();
        approveLog.setBusinessId(ArrayUtil.join(businessIds, ","));
        approveLog.setCreater(approveVo.getUserId());
        approveLog.setCreaterName("admin");
        approveLog.setCreateTime(new Date());
        approveLog.setType("02");
        approveLog.setApprovalStatus("01");
        approveLog.setRuleClass(approveVo.getRuleClass());
        String auditType = approveVo.getAuditType();
        if (StrUtil.isNotBlank(auditType)){
            approveLog.setAuditType(Integer.valueOf(auditType));
        }
        approveLog.setApprovalName("admin");
        approveLog.setApprovalRemark("系统默认审核通过!");
        approveLogMapper.insert(approveLog);

        return approveProcessCallBack.completedForProcess(businessApproveVo);
    }

    @Override
    public void autoApproveForAddSign(ApproveDetail approveDetail, String userId) {
        ApproveDetail record = new ApproveDetail();
        record.setId(approveDetail.getId());
        record.setApprovalStatus("03");
        record.setApprovalTime(new Date());
        record.setApprovalRemark("加签默认审核通过!");
        record.setApprovalResult("01");
        record.setUpdater(userId);
        record.setUpdateTime(new Date());
        detailService.updateById(record);

        ApproveLog approveLog = new ApproveLog();
        approveLog.setRuleNo(approveDetail.getRuleNo());
        approveLog.setBusinessId(approveDetail.getApproveNo());
        approveLog.setCreater(userId);
        approveLog.setApprovalDetailId(approveDetail.getId());
        approveLog.setApprovalName(approveDetail.getApproverName());
        approveLog.setCreaterName(userService.getById(userId).getName());
        approveLog.setCreateTime(new Date());
        approveLog.setType("02");
        approveLog.setApprovalStatus("01");
        approveLog.setRuleClass(approveDetail.getRuleClass());
        if (StrUtil.isNotBlank(approveDetail.getAuditType())) {
            approveLog.setAuditType(Integer.valueOf(approveDetail.getAuditType()));
        }
        approveLog.setApprovalRemark("加签默认审核通过!");

        approveLogMapper.insert(approveLog);
    }

}
